package net.avcompris.binding.dom.impl;

import static com.avcompris.util.ExceptionUtils.nonNullArgument;

import java.io.IOException;
import java.io.Writer;

import net.avcompris.binding.impl.AbstractToWriterSerializer;

import org.w3c.dom.CDATASection;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;

import com.avcompris.lang.NotImplementedException;
import com.avcompris.util.XMLUtils;

public class DomToWriterSerializer extends AbstractToWriterSerializer<Node> {

	public DomToWriterSerializer(final Writer writer) {

		super(writer);
	}

	@Override
	public void serialize(final Node node) throws IOException {

		dumpNode(node);
	}

	private void dumpNode(final Node node) throws IOException {

		nonNullArgument(node, "node");

		final int nodeType = node.getNodeType();

		switch (nodeType) {

		case Node.CDATA_SECTION_NODE:
			dumpCDATASection((CDATASection) node);
			break;

		case Node.ELEMENT_NODE:
			dumpElement((Element) node);
			break;

		case Node.TEXT_NODE:
			dumpText((Text) node);
			break;

		case Node.DOCUMENT_NODE:
			dumpDocument((Document) node);
			break;

		default:
			throw new NotImplementedException("Node: " + node.getNodeName());
		}
	}

	private void dumpCDATASection(final CDATASection cdataSection) throws IOException {

		nonNullArgument(cdataSection, "cdataSection");

		writer.write("<![CDATA[");
		writer.write(cdataSection.getNodeValue());
		writer.write("]]>");
	}

	private void dumpText(final Text text) throws IOException {

		nonNullArgument(text, "text");

		writer.write(XMLUtils.xmlEncode(text.getNodeValue()));
	}

	private void dumpDocument(final Document document) throws IOException {

		nonNullArgument(document, "document");

		dumpElement(document.getDocumentElement());
	}

	private void dumpElement(final Element element) throws IOException {

		nonNullArgument(element, "element");

		final String tagName = element.getTagName();

		//	writer.write(indent);
		writer.write('<');
		writer.write(tagName);

		final NamedNodeMap attributes = element.getAttributes();

		final int attributeCount = attributes.getLength();

		for (int i = 0; i < attributeCount; ++i) {

			final Node attribute = attributes.item(i);

			final String attributeName = attribute.getNodeName();
			final String attributeValue = attribute.getNodeValue();

			writer.write(' ');
			writer.write(attributeName);
			writer.write('=');
			writer.write('"');
			writer.write(XMLUtils.xmlEncode(attributeValue));
			writer.write('"');
		}

		if (!element.hasChildNodes()) {

			writer.write("/>");

			return;
		}

		writer.write('>');

		final NodeList children = element.getChildNodes();

		final int childCount = children.getLength();

		for (int i = 0; i < childCount; ++i) {

			final Node child = children.item(i);

			dumpNode(child);
		}

		writer.write("</");
		writer.write(tagName);
		writer.write('>');
	}
}
